package com.cogent.common.util;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import com.cogent.common.exception.SystemRuntimeException;
import com.cogent.common.reflect.BeanTypeDeal;

/**
 * 提供对XML操作的封装。</br>
 * 利用dom4j对xml操作进行封装。
 * @author daiys 
 * @version 1.0<br/>
 * ****************************************************<br/>
 * 版本号	 修改日期		作者			更改原因<br/>
 * 1.0		2009-11-25	daiys		新增<br/>
 * 1.1		2009-12-1	daiys		修改了attributeToBean和attributeToBean方法
 * ****************************************************<br/>
 */
public class XmlUtil {

	/**
	 * xml头说明
	 */
	private static final String XML_HEAD_STRING = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
	
	/**
	 * xml根节点
	 */
	private static final String XML_ROOT_STRING = "root";
	
	/**
	 * 从文本中加载xml对象
	 * @param xml xml文本
	 * @return 加载后的对象
	 */
	public static Document loadXml(String xml) {
		Document document = null;
		try {
			document = DocumentHelper.parseText(xml);
		} catch (DocumentException e) {
			throw new SystemRuntimeException(SystemRuntimeException.HIGH, e
					, "loadXml加载xml时异常, 文件为：" + xml, "loadXml加载xml时异常");
		}
		
		return document;
	}
	
	/**
	 * 从指定文件中加载xml对象
	 * @param fileName 文件名
	 * @return 加载后的对象
	 */
	public static Document loadXmlFormFile(String fileName) {
		Document document = null;
		SAXReader reader = new SAXReader();
		try {
			File f = new File(fileName);
			document = reader.read(f);
		} catch (DocumentException e) {
			throw new SystemRuntimeException(SystemRuntimeException.HIGH, e
					, "loadXmlFormFile加载xml时异常", "loadXmlFormFile加载xml时异常");
		}
		
		return document;
	}
	
	/**
	 * 获取指定元素，属性的值
	 * @param e 元素
	 * @param attribute 属性
	 * @return 指定属性的值
	 */
	public static String getAttributeValue(Element e, String attribute) {
		if(e.attribute(attribute) == null)
			return "";
		else
			return e.attribute(attribute).getData().toString();
	}
	
	/**
	 * 获取指定节点下，指定元素的值
	 * @param n 节点
	 * @param name 元素
	 * @return 元素的值
	 */
	public static String getNodeValue(Node n, String name) {
		Node temp = n.selectSingleNode(name);
		if(temp == null)
			return "";
		else
			return temp.getText();
	}
	
	/**
	 * 按属性解析xml，将其转换为相应的对象。<br/>
	 * 例如如下的XML节点<br/>
	 * <name="daiys" age="21" educate="本科"/><br/>
	 * 有一个对应的Bean类 PersonDomain,可以按如下方式解析<br/>
	 * PersonDomain pd = new PersonDomain();<br/>
	 * attributeToBean(element, pd);<br/>
	 * <p>并非所有的数据类型均支持转换，对象的数据类型支持参见BeanTypeDeal
	 * @see BeanTypeDeal
	 * @param element 属性节点
	 * @param bean 对象	 
	 */
	public static void attributeToBean(Element element, Object bean) {
		Class<?> classType = bean.getClass();
		Field[] fields = classType.getDeclaredFields();
		for(int i=0; i<fields.length; i++) {
			Field field = fields[i]; 
            String fieldName = field.getName();
            String type = field.getType().toString();
            if(!BeanTypeDeal.OMIT_TYPE_MAP.containsKey(type)) {
	            String stringLetter = fieldName.substring(0, 1).toUpperCase();    
	            String setName = "set"+stringLetter+fieldName.substring(1); 
	            try {
					Method setMethod=classType.getMethod(setName, new Class[]{field.getType()});
					setMethod.invoke(bean, BeanTypeDeal.getObject(type, getAttributeValue(element, fieldName)));    
				} catch (Throwable e) {
					throw new SystemRuntimeException(SystemRuntimeException.HIGH, e
							, "attributeToBean设置值时错误", "attributeToBean设置值时错误");
				}
            }
		}
	}
	
	/**
	 * 按节点解析xml，将其转换为相应的对象。<br/>
	 * 例如如下的XML节点<br/>
	 * <data><br/>
	 * <name>daiys</name><br/>
	 * <age>21</age><br/>
	 * <educate>本科</educate><br/>
	 * </data><br/>
	 * 有一个对应的Bean类 PersonDomain,可以按如下方式解析<br/>
	 * PersonDomain pd = new PersonDomain();<br/>
	 * nodeToBean(element, pd);
	 * <p>并非所有的数据类型均支持转换，对象的数据类型支持参见BeanTypeDeal
	 * @see BeanTypeDeal
	 * @param n 元素节点
	 * @param bean 对象
	 */
	public static void nodeToBean(Node n, Object bean) {
		Class<?> classType = bean.getClass();
		Field[] fields = classType.getDeclaredFields();
		for(int i=0; i<fields.length; i++) {
			Field field = fields[i]; 
            String fieldName = field.getName(); 
            String type = field.getType().toString();
            if(!BeanTypeDeal.OMIT_TYPE_MAP.containsKey(type)) {
	            String stringLetter = fieldName.substring(0, 1).toUpperCase();    
	            String setName = "set"+stringLetter+fieldName.substring(1); 
	            try {
					Method setMethod=classType.getMethod(setName, new Class[]{field.getType()});
					setMethod.invoke(bean, BeanTypeDeal.getObject(type, getNodeValue(n, fieldName)));    
				} catch (Throwable e) {
					throw new SystemRuntimeException(SystemRuntimeException.HIGH, e
							, "nodeToBean设置值时错误", "nodeToBean设置值时错误");
				}
            }
		}
	}
	
	/**
	 * 将Bean属性转换为xml属性节点。<br/>
	 * 例如PersonDomain,有以下属性：<br/>
	 * String name 赋值为 daiys<br/>
	 * ing age 赋值为 21<br/>
	 * String educate 赋值为 本科<br/>
	 * 调用beanToAttribute(personDomain)转换后，xml文本如下：<br/>
	 * <name="daiys" age="21" educate="本科"/><br/>
	 * <p>并非所有的数据类型均支持转换，对象的数据类型支持参见BeanTypeDeal
	 * @see BeanTypeDeal
	 * @param bean 待转换的bean
	 * @return 转换后的xml文本
	 */
	public static String beanToAttribute(Object bean) {
		StringBuffer str = new StringBuffer();
		str.append("<");
		Class<?> classType = bean.getClass();
		Field[] fields = classType.getDeclaredFields();
		int num = fields.length - 1;
		for(int i=0; i<fields.length; i++) { 
            fields[i].setAccessible(true);
            Object object = null;
            str.append(fields[i].getName()).append("=\"");
            try {
				object = fields[i].get(bean);
			} catch (Throwable e) {
				throw new SystemRuntimeException(SystemRuntimeException.HIGH, e
						, "beanToAttribute转换值时错误", "beanToAttribute转换值时错误");
			}
            str.append(BeanTypeDeal.getString(fields[i].getType().toString(), object))
            	.append("\"");
            if(i < num)
            	str.append(" ");
		}
		str.append("/>");
		
		return str.toString();
	}
	
	/**
	 * 将Bean属性转换为xml节点。<br/>
	 * 例如PersonDomain,有以下属性：<br/>
	 * String name 赋值为 daiys<br/>
	 * ing age 赋值为 21<br/>
	 * String educate 赋值为 本科<br/>
	 * 调用beanToNode(personDomain, "data")转换后，xml文本如下：<br/>
	 * <data><br/>
	 * <name>daiys</name><br/>
	 * <age>21</age><br/>
	 * <educate>本科</educate><br/>
	 * </data><br/>
	 * <p>并非所有的数据类型均支持转换，对象的数据类型支持参见BeanTypeDeal
	 * @see BeanTypeDeal
	 * @param bean 待转换的bean
	 * @param parentNodeName 父节点名称
	 * @return 转换后的xml文本
	 */
	public static String beanToNode(Object bean, String parentNodeName) {
		StringBuffer str = new StringBuffer();
		str.append("<").append(parentNodeName).append(">\n");
		Class<?> classType = bean.getClass();
		Field[] fields = classType.getDeclaredFields();
		for(int i=0; i<fields.length; i++) { 
            fields[i].setAccessible(true);
            Object object = null;
            str.append("\t<").append(fields[i].getName()).append(">");
            try {
				object = fields[i].get(bean);
			} catch (Throwable e) {
				throw new SystemRuntimeException(SystemRuntimeException.HIGH, e
						, "beanToNode转换值时错误", "beanToNode转换值时错误");
			}
            str.append(BeanTypeDeal.getString(fields[i].getType().toString(), object))
            	.append("</").append(fields[i].getName()).append(">\n");
		}
		str.append("</").append(parentNodeName).append(">");
		
		return str.toString();
	}
	
	/**
	 * 将List封装的bean转换为xml属性文本.</br>
	 * 例如PersonDomain,personDomain1对象有以下属性：<br/>
	 * String name 赋值为 daiys<br/>
	 * ing age 赋值为 21<br/>
	 * String educate 赋值为 本科<br/>
	 * personDomain2对象有以下属性：<br/>
	 * String name 赋值为 pancc<br/>
	 * ing age 赋值为 16<br/>
	 * String educate 赋值为 本科<br/>
	 * 将personDomain1, personDomain2添加到List对象list中<br/>
	 * 调用listToAttribute(list)转换后，xml文本如下：<br/>
	 * <name="daiys" age="21" educate="本科"/><br/>
	 * <name="pancc" age="16" educate="本科"/><br/>
	 * <p>并非所有的数据类型均支持转换，对象的数据类型支持参见BeanTypeDeal
	 * @see BeanTypeDeal
	 * @param list 待转换的List
	 * @return 转换后的xml文本
	 */
	public static String listToAttribute(List<Object> list) {
		StringBuffer str = new StringBuffer();
		for(Object obj : list) {
			str.append(beanToAttribute(obj)).append("\n");
		}
		
		return str.toString();
	}
	
	/**
	 * 将List封装的bean转换为xml节点文本.</br>
	 * 例如PersonDomain,personDomain1对象有以下属性：<br/>
	 * String name 赋值为 daiys<br/>
	 * ing age 赋值为 21<br/>
	 * String educate 赋值为 本科<br/>
	 * personDomain2对象有以下属性：<br/>
	 * String name 赋值为 pancc<br/>
	 * ing age 赋值为 16<br/>
	 * String educate 赋值为 本科<br/>
	 * 将personDomain1, personDomain2添加到List对象list中<br/>
	 * 调用listToNode(list, "data")转换后，xml文本如下：<br/>
	 * <data><br/>
	 * <name>daiys</name><br/>
	 * <age>21</age><br/>
	 * <educate>本科</educate><br/>
	 * </data><br/>
	 * <data><br/>
	 * <name>pancc</name><br/>
	 * <age>16</age><br/>
	 * <educate>本科</educate><br/>
	 * </data><br/>
	 * <p>并非所有的数据类型均支持转换，对象的数据类型支持参见BeanTypeDeal
	 * @see BeanTypeDeal
	 * @param list 待转换的list
	 * @param parentNodeName 父节点名称
	 * @return 转换后的xml文本
	 */
	public static String listToNode(List<Object> list, String parentNodeName) {
		StringBuffer str = new StringBuffer();
		for(Object obj : list) {
			str.append(beanToNode(obj, parentNodeName)).append("\n");
		}
		
		return str.toString();
	}
	
	/**
	 * 将xml属性节点转换为Map.<br/>
	 * 例如如下的XML节点<br/>
	 * <name="daiys" age="21" educate="本科"/><br/>
	 * 调用attributeToMap(element)解析后,map的键值对为<br/>
	 * name=daiys, age=21, educate=本科</br>
	 * PersonDomain pd = new PersonDomain();<br/>
	 * @param element 属性节点
	 * @return map对象
	 */
	public static Map<String, String> attributeToMap(Element element) {
		Map<String, String> map = new HashMap<String, String>();
		Iterator<?> it = element.attributeIterator();
		while(it.hasNext()) {
			Attribute attribute = (Attribute) it.next(); 
			map.put(attribute.getName(), attribute.getData().toString());
		}
		
		return map;
	}
	
	/**
	 * 将bean转换为xml文本.<br/>
	 * 例如PersonDomain,personDomain对象有以下属性：<br/>
	 * String name 赋值为 daiys<br/>
	 * ing age 赋值为 21<br/>
	 * String educate 赋值为 本科<br/>
	 * 调用beanToXml(personDomain)后，其xml文本如下：<br/>
	 * <?xml version=\"1.0\" encoding=\"UTF-8\"?><br/>
	 * <root><br/>
	 * <name="daiys" age="21" educate="本科"/><br/>
	 * </root><br/>
	 * <p>并非所有的数据类型均支持转换，对象的数据类型支持参见BeanTypeDeal
	 * @see BeanTypeDeal
	 * @param obj 待转换的bean
	 * @return xml文本
	 */
	public static String beanToXml(Object obj) {
		StringBuffer str = new StringBuffer();
		str.append(XML_HEAD_STRING).append("<").append(XML_ROOT_STRING)
			.append(">\n\t").append(beanToAttribute(obj)).append("\n</")
			.append(XML_ROOT_STRING).append(">");
		
		return str.toString();
	}
	
	/**
	 * 将xml文本转换为bean.<br/>
	 * 例如如下xml文本.<br/>
	 * <?xml version=\"1.0\" encoding=\"UTF-8\"?><br/>
	 * <root><br/>
	 * <name="daiys" age="21" educate="本科"/><br/>
	 * </root><br/>
	 * 调用xmlToBean(xml, personDomain)后personDomain中的值为:<br/>
	 * String name 赋值为 daiys<br/>
	 * ing age 赋值为 21<br/>
	 * String educate 赋值为 本科<br/>
	 * <p>并非所有的数据类型均支持转换，对象的数据类型支持参见BeanTypeDeal
	 * @see BeanTypeDeal
	 * @param xml 待转换的xml文本
	 * @param obj 转换后的bean
	 */
	public static void xmlToBean(String xml, Object obj) {
		Document document = loadXml(xml);
		Element el = (Element)document.selectSingleNode("//" + XML_ROOT_STRING);
		Element beanElement = null;
		for(int i= 0; i<el.nodeCount(); i++){
			Node n = el.node(i);
			if(n instanceof Element)
				beanElement = (Element)n;
		}
		
		attributeToBean(beanElement, obj);
	}
	
	/**
	 * 将xml文本转换为键值对形式.<br/>
	 * 例如如下xml文本.<br/>
	 * <?xml version=\"1.0\" encoding=\"UTF-8\"?><br/>
	 * <root><br/>
	 * <name="daiys" age="21" educate="本科"/><br/>
	 * </root><br/>
	 * 调用xmlToMap(xml)后map的键值对为<br/>
	 * name=daiys, age=21, educate=本科</br>
	 * @param xml 待转换的xml文本
	 * @return 转换后的map
	 */
	public static Map<String, String> xmlToMap(String xml) {
		Document document = loadXml(xml);
		Element el = (Element)document.selectSingleNode("//" + XML_ROOT_STRING);
		Element beanElement = null;
		for(int i= 0; i<el.nodeCount(); i++){
			Node n = el.node(i);
			if(n instanceof Element)
				beanElement = (Element)n;
		}
		
		return attributeToMap(beanElement);
	}
	
	public static String getCdataValue(Node node, String xpath) {
		 Node sqlNode = node.selectSingleNode(xpath);
		 return getCdataValue(sqlNode);
	}
	
	public static  String getCdataValue(Node node) {
		 String ret = "";
		 if(node != null)
			 ret = node.getText().trim();
		 
		 return ret;
	}
}
